import re
import json
from typing import Dict, Any, Optional, Tuple, List
from langchain_openai import ChatOpenAI
from langchain.schema import HumanMessage

class NLPTool:
    def __init__(self, llm: Optional[ChatOpenAI] = None):
        self.llm = llm
        # 定义商品类别对应的单位
        self.category_units = {
            "水果": "斤",
            "蔬菜": "斤",
            "肉类": "斤",
            "海鲜": "斤",
            "乳制品": "盒",
            "饮品": "瓶",
            "零食": "包",
            "家居用品": "件",
            "电子产品": "台",
            "运动用品": "个",
            "烘焙": "个"
        }
        
        # 定义场景与商品类别的映射关系
        self.scene_category_mapping = {
            "健身": ["运动用品", "健身器材", "蛋白质饮品", "肉类", "水果", "蔬菜"],
            "学习": ["电子产品", "文具", "饮品", "零食"],
            "做饭": ["厨具", "食材", "烘焙", "蔬菜", "肉类"],
            "清洁": ["清洁用品", "家居用品"],
            "厨房": ["厨具", "家居用品", "食材"],
            "烹饪": ["厨具", "食材", "烘焙", "蔬菜", "肉类"],
            "运动": ["运动用品", "健身器材", "饮品", "水果"],
            "办公": ["电子产品", "文具", "饮品"],
            "购物": ["水果", "蔬菜", "乳制品", "肉类", "饮品", "零食"],
            "饮食": ["水果", "蔬菜", "乳制品", "肉类", "饮品", "零食", "烘焙"],
            "减肥": ["水果", "蔬菜", "低脂食品", "运动用品", "健身器材"],
            "增肌": ["肉类", "蛋白质饮品", "运动用品", "健身器材"],
            "瑜伽": ["运动用品", "瑜伽垫", "瑜伽服"],
            "跑步": ["运动用品", "运动鞋", "运动服装", "饮品"],
            "游泳": ["运动用品", "泳衣", "泳镜", "泳帽"],
            "球类运动": ["运动用品", "篮球", "足球", "羽毛球", "乒乓球"],
            "户外活动": ["运动用品", "户外装备", "饮品", "零食"],
            "居家健身": ["健身器材", "运动用品", "运动服装"]
        }
        
        # 定义场景关键词映射
        self.scene_keywords = {
            "健身": ["健身", "锻炼", "运动", "健康", "肌肉", "力量", "有氧", "无氧", "增肌", "减脂", "健身房", "器械", "哑铃", "杠铃", "跑步机"],
            "学习": ["学习", "读书", "考试", "上课", "笔记", "作业", "论文", "研究", "教育", "知识", "学校", "大学", "课程"],
            "做饭": ["做饭", "烹饪", "厨房", "煮", "炒", "蒸", "煎", "炸", "烤", "菜谱", "食材", "调料", "厨具"],
            "清洁": ["清洁", "打扫", "卫生", "洗涤", "擦拭", "消毒", "除尘", "除菌", "洗衣", "洗碗", "拖地"],
            "运动": ["运动", "跑步", "游泳", "篮球", "足球", "羽毛球", "乒乓球", "健身", "瑜伽", "户外", "骑行", "登山"],
            "办公": ["办公", "工作", "会议", "文档", "报表", "演示", "项目", "计划", "电脑", "笔记本", "打印", "文具"],
            "购物": ["购物", "买东西", "逛街", "选购", "比价", "促销", "打折", "优惠", "超市", "商场", "网购"],
            "饮食": ["饮食", "吃饭", "餐饮", "美食", "营养", "健康饮食", "食谱", "菜品", "水果", "蔬菜", "肉类", "零食"],
            "减肥": ["减肥", "减脂", "瘦身", "塑形", "节食", "控制饮食", "低卡", "低脂", "低糖", "代餐"],
            "增肌": ["增肌", "增重", "健美", "肌肉", "蛋白质", "力量训练", "重训", "高蛋白", "补剂"],
            "瑜伽": ["瑜伽", "冥想", "伸展", "柔韧", "平衡", "瑜伽垫", "瑜伽服", "体式"],
            "跑步": ["跑步", "慢跑", "马拉松", "跑鞋", "跑步机", "有氧", "耐力", "步频", "步幅"],
            "游泳": ["游泳", "泳池", "泳衣", "泳镜", "泳帽", "自由泳", "蛙泳", "仰泳", "蝶泳"],
            "球类运动": ["篮球", "足球", "排球", "网球", "乒乓球", "羽毛球", "高尔夫", "球鞋", "球拍", "球衣"],
            "户外活动": ["户外", "露营", "徒步", "登山", "骑行", "钓鱼", "野餐", "帐篷", "背包", "户外装备"],
            "居家健身": ["居家健身", "家庭健身", "健身器材", "健身app", "健身视频", "瑜伽垫", "哑铃", "弹力带", "俯卧撑", "仰卧起坐"]
        }
        
        # 定义场景与商品的详细映射关系
        self.scene_product_mapping = {
            "健身": {
                "运动用品": ["篮球", "足球", "羽毛球", "乒乓球", "哑铃", "杠铃", "跑步机", "健身器材"],
                "饮品": ["蛋白质饮品", "能量饮料", "矿泉水", "运动饮料"],
                "食品": ["蛋白棒", "能量棒", "坚果", "水果", "全麦面包"],
                "服装": ["运动服", "运动鞋", "运动袜", "运动内衣"]
            },
            "减肥": {
                "食品": ["低脂食品", "代餐", "水果", "蔬菜", "全麦食品"],
                "饮品": ["无糖饮料", "绿茶", "黑咖啡", "果蔬汁"],
                "运动用品": ["有氧运动器材", "跑步机", "健身车", "计步器"],
                "厨具": ["蒸锅", "煎锅", "食物秤", "搅拌机"]
            },
            "增肌": {
                "食品": ["高蛋白食品", "肉类", "蛋类", "奶制品", "坚果"],
                "饮品": ["蛋白质粉", "增肌粉", "牛奶", "蛋白饮料"],
                "运动用品": ["哑铃", "杠铃", "健身器材", "握力器"],
                "补剂": ["蛋白粉", "肌酸", "BCAA", "氨基酸"]
            }
        }

    def analyze_intent(self, query: str) -> Dict[str, Any]:
        """分析用户意图"""
        try:
            if not self.llm:
                raise Exception("未配置大模型，无法进行意图分析")

            prompt = f"""
            请分析以下用户查询的意图。请仔细理解用户的真实需求，不要被表面的词语迷惑。

            用户查询: "{query}"

            请从以下几个方面分析：
            1. 核心意图：用户真正想要做什么
            2. 场景理解：用户在什么场景下提出这个需求（如健身、学习、做饭、清洁、运动、办公、购物、饮食等）
            3. 具体需求：包括商品、数量、支付方式等具体信息
            4. 隐含信息：用户可能未直接表达但暗含的需求

            意图类型必须是以下之一：
            1. greeting - 问候（如：你好、您好等）
            2. recommendation - 商品推荐（如：推荐水果、有什么好吃的、健身用什么器材好）
            3. purchase - 购买意图（如：买苹果、要10斤香蕉）
            4. payment - 支付意图（如：现金支付、用微信付款）
            5. inventory - 库存查询（如：还有多少苹果、有没有香蕉）
            6. general_question - 一般性问题（如：怎么用、如何购买）

            请特别注意：
            - 如果用户提到"健身"、"锻炼"、"运动"等词语，这可能是一个场景描述，用户可能在寻求与健身相关的商品推荐
            - 如果用户提到"学习"、"办公"等词语，这可能是一个场景描述，用户可能在寻求与学习或办公相关的商品推荐
            - 如果用户提到"做饭"、"烹饪"等词语，这可能是一个场景描述，用户可能在寻求与厨房相关的商品推荐
            - 如果用户提到"减肥"、"瘦身"等词语，这可能是一个场景描述，用户可能在寻求与减肥相关的商品推荐
            - 如果用户提到"增肌"、"增重"等词语，这可能是一个场景描述，用户可能在寻求与增肌相关的商品推荐

            请返回以下格式的JSON（不要包含任何其他文字）：
            {{
                "type": "意图类型",
                "query": "原始查询",
                "confidence": 0.0-1.0的置信度,
                "parameters": {{
                    "scene": "使用场景",
                    "category": "商品类别",
                    "product": ["具体商品1", "具体商品2"],
                    "quantity": [数量1, 数量2],
                    "unit": ["单位1", "单位2"],
                    "payment_method": "支付方式",
                    "price_range": {{"min": 最小价格, "max": 最大价格}},
                    "requirements": ["特殊要求1", "特殊要求2"]
                }},
                "message": "根据意图类型生成的合适回复"
            }}
            """

            response = self.llm.invoke([HumanMessage(content=prompt)])
            content = response.content.strip()

            # 清理响应内容
            if "```json" in content:
                content = content.split("```json")[1].split("```")[0].strip()
            elif "```" in content:
                content = content.split("```")[1].strip()

            # 解析JSON
            intent = json.loads(content)
            
            # 增强场景分析
            if intent["type"] == "recommendation" and "parameters" in intent:
                # 如果没有识别出场景，尝试从查询中识别
                if not intent["parameters"].get("scene"):
                    scene = self.detect_scene_from_query(query)
                    if scene:
                        intent["parameters"]["scene"] = scene
                
                # 如果识别出场景但没有类别，添加相关类别
                if intent["parameters"].get("scene") and not intent["parameters"].get("category"):
                    scene = intent["parameters"]["scene"]
                    categories = self.get_categories_for_scene(scene)
                    if categories:
                        intent["parameters"]["category"] = categories[0]  # 使用第一个相关类别
            
            return intent

        except Exception as e:
            print(f"分析意图时出错: {str(e)}")
            return {
                "type": "error",
                "message": "抱歉，系统处理您的请求时出现错误，请重试。"
            }
    
    def detect_scene_from_query(self, query: str) -> Optional[str]:
        """从查询中检测场景"""
        query_lower = query.lower()
        
        # 检查每个场景的关键词
        matched_scenes = {}
        
        for scene, keywords in self.scene_keywords.items():
            match_count = 0
            for keyword in keywords:
                if keyword in query_lower:
                    match_count += 1
            
            if match_count > 0:
                matched_scenes[scene] = match_count
        
        # 如果有匹配的场景，返回匹配度最高的
        if matched_scenes:
            return max(matched_scenes.items(), key=lambda x: x[1])[0]
        
        return None
    
    def get_categories_for_scene(self, scene: str) -> List[str]:
        """获取场景相关的商品类别"""
        return self.scene_category_mapping.get(scene, [])
    
    def get_products_for_scene(self, scene: str) -> Dict[str, List[str]]:
        """获取场景相关的具体商品"""
        return self.scene_product_mapping.get(scene, {})

    def parse_purchase_intent(self, query: str) -> Dict[str, Any]:
        """解析购买意图"""
        try:
            if not self.llm:
                raise Exception("未配置大模型，无法解析购买意图")

            prompt = f"""
            请仔细分析以下购买请求，提取所有相关信息。注意要理解中文表达的多样性和隐含信息。

            用户请求：{query}

            请分析以下要素：
            1. 购买的商品和数量
            2. 支付方式（如果提到）
            3. 商品单位（斤、个、件等）
            4. 特殊要求或限制
            5. 隐含的购买意图

            请返回以下格式的JSON（不要包含任何其他文字）：
            {{
                "success": true,
                "confidence": 0.0-1.0的置信度,
                "items": [
                    {{
                        "product_name": "商品名称",
                        "quantity": 数量,
                        "unit": "单位",
                        "confidence": 0.0-1.0的置信度
                    }}
                ],
                "payment_method": "支付方式",
                "payment_confidence": 0.0-1.0的置信度,
                "requirements": ["特殊要求1", "特殊要求2"],
                "message": "根据分析结果生成的友好回复"
            }}

            注意：
            1. 数量必须是数字
            2. 单位必须是：斤、个、件、包、瓶、盒 中的一个
            3. 支付方式必须是：现金、信用卡、移动支付 中的一个
            4. 如果用户未明确指定数量，默认为1
            5. 如果无法确定某个信息，相应的confidence设置为较低值
            6. 如果完全无法解析购买意图，返回 "success": false
            """

            response = self.llm.invoke([HumanMessage(content=prompt)])
            content = response.content.strip()

            # 清理响应内容
            if "```json" in content:
                content = content.split("```json")[1].split("```")[0].strip()
            elif "```" in content:
                content = content.split("```")[1].strip()

            # 解析JSON
            result = json.loads(content)
            
            # 验证结果格式
            if not isinstance(result, dict) or 'success' not in result or 'items' not in result:
                return {
                    'success': False,
                    'message': '解析结果格式错误',
                    'data': None
                }

            return result

        except Exception as e:
            print(f"解析购买意图时出错: {str(e)}")
            return {
                'success': False,
                'message': '解析购买意图时出错',
                'data': None
            }

    def parse_payment_method(self, query: str) -> Tuple[bool, str]:
        """解析支付方式"""
        try:
            if not self.llm:
                raise Exception("未配置大模型，无法解析支付方式")

            prompt = f"""
            请仔细分析用户输入中提到的支付方式。注意要理解中文表达的多样性。

            用户输入：{query}

            请分析以下要素：
            1. 明确提到的支付方式
            2. 隐含的支付方式
            3. 支付方式的表达形式（如：用现金买、现金支付、付现金等）
            4. 支付方式的确定程度

            请返回以下格式的JSON（不要包含任何其他文字）：
            {{
                "success": true/false,
                "payment_method": "现金/信用卡/移动支付",
                "confidence": 0.0-1.0的置信度,
                "original_expression": "用户原始表达",
                "message": "根据分析结果生成的友好回复"
            }}

            注意：
            1. 支付方式必须是：现金、信用卡、移动支付 中的一个
            2. 如果无法确定支付方式，返回 "success": false
            3. confidence反映支付方式识别的确定程度
            4. 需要考虑各种支付方式的表达形式，如：
               - 现金类：现金、付现金、用现金、给现金等
               - 信用卡类：信用卡、刷卡、银行卡等
               - 移动支付类：支付宝、微信、扫码、手机支付等
            """

            response = self.llm.invoke([HumanMessage(content=prompt)])
            content = response.content.strip()

            # 清理响应内容
            if "```json" in content:
                content = content.split("```json")[1].split("```")[0].strip()
            elif "```" in content:
                content = content.split("```")[1].strip()

            result = json.loads(content)
            if result.get("success") and result.get("confidence", 0) > 0.7:
                return True, result["payment_method"]
            return False, "未知支付方式"

        except Exception as e:
            print(f"解析支付方式时出错: {str(e)}")
            return False, "解析支付方式出错"

    def generate_recommendation_text(self, products: List[Dict], query: str) -> str:
        """生成商品推荐文案"""
        try:
            if not self.llm:
                # 如果没有配置大模型，使用简单的模板生成
                if not products:
                    return "抱歉，没有找到符合您需求的商品。"
                
                result = "为您推荐以下商品：\n"
                for i, product in enumerate(products[:5], 1):
                    result += f"{i}. {product['name']} - ¥{product['price']} - {product.get('product_description', '暂无描述')[:50]}...\n"
                return result
            
            # 检测查询中的场景
            scene = self.detect_scene_from_query(query)
            
            # 准备商品信息
            product_info = []
            for product in products[:5]:  # 只使用前5个商品
                info = {
                    "name": product["name"],
                    "price": product["price"],
                    "brand": product.get("brand", ""),
                    "category": product.get("category_name", ""),
                    "description": product.get("product_description", "暂无描述")
                }
                product_info.append(info)
            
            # 构建提示
            prompt = f"""
            请根据用户的查询和商品信息，生成一段个性化的商品推荐文案。
            
            用户查询: "{query}"
            
            {"检测到的场景: " + scene if scene else ""}
            
            商品信息:
            {json.dumps(product_info, ensure_ascii=False, indent=2)}
            
            请注意以下要点:
            1. 文案应该针对用户的具体需求和场景进行个性化推荐
            2. 突出商品的特点和优势，特别是与用户需求相关的方面
            3. 如果是特定场景（如健身、学习、做饭等），应该解释为什么这些商品适合该场景
            4. 使用自然、友好的语言，避免过度销售的感觉
            5. 可以适当提供使用建议或搭配推荐
            6. 文案长度适中，不要过长
            
            如果是健身场景，请特别说明:
            - 这些商品如何帮助用户达到健身目标
            - 可能的使用方法或训练建议
            - 营养价值或健康益处（如适用）
            
            如果是减肥场景，请特别说明:
            - 这些商品如何帮助用户达到减肥目标
            - 可能的热量或营养信息
            - 使用建议或饮食搭配
            
            如果是增肌场景，请特别说明:
            - 这些商品如何帮助用户增肌
            - 蛋白质含量或其他营养价值
            - 使用建议或训练搭配
            
            请直接返回文案内容，不要包含任何其他说明或格式标记。
            """
            
            response = self.llm.invoke([HumanMessage(content=prompt)])
            return response.content.strip()
            
        except Exception as e:
            print(f"生成推荐文案时出错: {str(e)}")
            # 出错时使用简单模板
            if not products:
                return "抱歉，没有找到符合您需求的商品。"
            
            result = "为您推荐以下商品：\n"
            for i, product in enumerate(products[:5], 1):
                result += f"{i}. {product['name']} - ¥{product['price']}\n"
            return result

    def generate_scene_specific_recommendation(self, scene: str, products: List[Dict]) -> str:
        """生成特定场景的商品推荐文案"""
        try:
            if not self.llm or not products:
                return self.generate_recommendation_text(products, f"推荐适合{scene}的商品")
            
            # 准备商品信息
            product_info = []
            for product in products[:5]:  # 只使用前5个商品
                info = {
                    "name": product["name"],
                    "price": product["price"],
                    "brand": product.get("brand", ""),
                    "category": product.get("category_name", ""),
                    "description": product.get("product_description", "暂无描述")
                }
                product_info.append(info)
            
            # 获取场景特定的提示信息
            scene_prompts = {
                "健身": """
                请特别说明:
                - 这些商品如何帮助用户达到健身目标
                - 可能的使用方法或训练建议
                - 营养价值或健康益处（如适用）
                - 不同健身阶段或目标的适用性
                """,
                "减肥": """
                请特别说明:
                - 这些商品如何帮助用户达到减肥目标
                - 可能的热量或营养信息
                - 使用建议或饮食搭配
                - 如何融入减肥计划
                """,
                "增肌": """
                请特别说明:
                - 这些商品如何帮助用户增肌
                - 蛋白质含量或其他营养价值
                - 使用建议或训练搭配
                - 最佳使用时机
                """,
                "学习": """
                请特别说明:
                - 这些商品如何提高学习效率
                - 使用建议或学习方法
                - 适合的学习场景
                """,
                "做饭": """
                请特别说明:
                - 这些商品如何提升烹饪体验
                - 可能的食谱或烹饪建议
                - 使用技巧或搭配方案
                """
            }
            
            scene_prompt = scene_prompts.get(scene, "")
            
            # 构建提示
            prompt = f"""
            请根据特定场景和商品信息，生成一段个性化的商品推荐文案。
            
            场景: {scene}
            
            商品信息:
            {json.dumps(product_info, ensure_ascii=False, indent=2)}
            
            请注意以下要点:
            1. 文案应该针对"{scene}"场景进行个性化推荐
            2. 突出商品的特点和优势，特别是与该场景相关的方面
            3. 解释为什么这些商品适合该场景
            4. 使用自然、友好的语言，避免过度销售的感觉
            5. 可以适当提供使用建议或搭配推荐
            6. 文案长度适中，不要过长
            
            {scene_prompt}
            
            请直接返回文案内容，不要包含任何其他说明或格式标记。
            """
            
            response = self.llm.invoke([HumanMessage(content=prompt)])
            return response.content.strip()
            
        except Exception as e:
            print(f"生成场景推荐文案时出错: {str(e)}")
            return self.generate_recommendation_text(products, f"推荐适合{scene}的商品")

    def generate_response(self, intent_type: str, data: Dict[str, Any]) -> str:
        """生成响应文本"""
        if not self.llm:
            if intent_type == "purchase_success":
                if "products" in data:
                    # 多商品场景
                    products_text = "\n".join([
                        f"- {p['product_name']}: {p['quantity']}{p['unit']}，金额：{p['amount']}元"
                        for p in data['products']
                    ])
                    return f"购买成功！\n商品明细：\n{products_text}\n总金额：{data['total_amount']}元\n支付方式：{data['payment_method']}\n感谢您的购买，欢迎再次光临！"
                else:
                    # 单商品场景
                    return f"购买成功！共支付{data['amount']}元，感谢您的购买，欢迎再次光临！"
            elif intent_type == "purchase_failed":
                return f"抱歉，购买失败：{data.get('reason', '未知原因')}"
            elif intent_type == "restock_success":
                return f"补货成功！已添加{data['quantity']}{data['unit']}{data['product_name']}到库存。"
            elif intent_type == "restock_failed":
                return f"补货失败：{data.get('reason', '未知原因')}"
            return "抱歉，我不太理解您的意思。"

        try:
            if intent_type == "purchase_success":
                # 构建商品明细文本
                products_info = ""
                if "products" in data:
                    products_info = "商品明细：\n" + "\n".join([
                        f"- {p['product_name']}: {p['quantity']}{p['unit']}，金额：{p['amount']}元"
                        for p in data.get('products', [])
                    ])
                else:
                    products_info = f"- {data['product_name']}: {data['quantity']}{data['unit']}，金额：{data['amount']}元"

                prompt = f"""
                用户成功购买了商品，请生成一个友好的回复。

                购买信息：
                {products_info}
                总金额：{data.get('total_amount', data.get('amount'))}元
                支付方式：{data['payment_method']}

                要求：
                1. 确认购买成功
                2. 列出商品明细
                3. 说明总金额
                4. 表达感谢
                5. 欢迎再次光临
                6. 语气要温暖友好
                7. 使用中文回复
                """
            elif intent_type == "purchase_failed":
                prompt = f"""
                用户购买失败，请生成一个友好的回复。

                失败信息：
                - 商品：{data['product_name']}
                - 数量：{data['quantity']}{data['unit']}
                - 失败原因：{data.get('reason', '未知原因')}

                要求：
                1. 表达歉意
                2. 说明失败原因
                3. 如果是库存不足，告知当前库存量
                4. 给出建设性建议
                5. 语气要诚恳
                6. 使用中文回复
                """
            elif intent_type == "restock_success":
                prompt = f"""
                成功补充了商品库存，请生成一个确认回复。

                补货信息：
                - 商品：{data['product_name']}
                - 补充数量：{data['quantity']}{data['unit']}
                - 当前库存：{data.get('current_stock', '未知')}

                要求：
                1. 确认补货成功
                2. 说明补充数量
                3. 如果有当前库存信息，一并告知
                4. 语气要专业
                5. 使用中文回复
                """
            elif intent_type == "restock_failed":
                prompt = f"""
                补充库存失败，请生成一个解释性回复。

                失败信息：
                - 商品：{data['product_name']}
                - 计划补充数量：{data['quantity']}{data['unit']}
                - 失败原因：{data.get('reason', '未知原因')}

                要求：
                1. 说明失败原因
                2. 给出改进建议
                3. 语气要专业且有建设性
                4. 使用中文回复
                """
            else:
                return "抱歉，我不太理解您的意思。"

            response = self.llm.invoke([HumanMessage(content=prompt)])
            return response.content.strip()

        except Exception as e:
            print(f"生成响应文本时出错: {str(e)}")
            return "抱歉，生成响应时出现错误。"